/*
 * This sample is released as public domain.  It is distributed in the hope that 
 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 * 
 * Authors:
 * Jeremiah Morrill (jeremiah.morrill@gmail.com)
 * Leslie Godwin (leslie.godwin@gmail.com)
 *
 * Win32HostRenderer is a WPF control that will host a Win32 control and render it onto a WPF bitmap buffer.
 * 
 * 2007-08-01:
 * Initial release.
 * 2007-08-04:
 * Bug-fixes.
 * 1. Win32 Control is now only active when the mouse is over the WPF Control. (even in 3D Mode, so the mouse cursor works properly)
 * 2. The control refreshes only when the control is actually visible.
 * 3. The shutdown bug seems to have been fixed. (Though I have a hunch I have only kiched it in the groin.)
 * 4. The mouse messages are handled a bit better.
 * 5. Some somespace changes. 
 *
 */

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Reflection;
using System.Runtime.InteropServices;

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using System.ComponentModel;

using _3DTools;
using Win32;

using Winforms = System.Windows.Forms;
using GDI = System.Drawing;
using PixelFormat = System.Drawing.Imaging.PixelFormat;
using Point = System.Windows.Point;
using Size = System.Windows.Size;

namespace WPFInterop.Interop
{
    public class Win32HostRenderer : Control,
        IMouseHookUser, 
        Winforms.IMessageFilter,
        INotifyPropertyChanged
    {
        // static cache for the mrthods that need to be invoked via reflection.
        private static readonly FieldInfo _fieldHiddenVisual;
        private static readonly MethodInfo _methArrangeHiddenVisual;
        private static readonly PropertyInfo _propInheritanceParent;

        /// <summary>
        /// Initializes the <see cref="Win32HostRenderer"/> class static info.
        /// </summary>
        /// 
        /// <logic>
        /// 	<item>1. Cache all the member hacks.</item>
        /// </logic>
        /// 
        static Win32HostRenderer()
        {
            // 1
            // Even Cached invoking these calls will be slow.
            // TODO: rather create dynamic native calls to the methods.
            _propInheritanceParent = typeof(DependencyObject).GetProperty("InheritanceParent",
                                                                           BindingFlags.NonPublic
                                                                         | BindingFlags.Instance);

            if (_propInheritanceParent == null)
                throw new InvalidOperationException("InheritanceParent property" + Properties.Resources.WPF_LIB_NAME);

            _fieldHiddenVisual = typeof(Interactive3DDecorator).GetField("_hiddenVisual",
                                                                          BindingFlags.NonPublic
                                                                        | BindingFlags.Instance);

            if (_fieldHiddenVisual == null)
                throw new InvalidOperationException("_hiddenVisual field" + Properties.Resources.WPF_LIB_NAME);

            _methArrangeHiddenVisual = typeof(Interactive3DDecorator).GetMethod("ArrangeHiddenVisual",
                                                                                  BindingFlags.NonPublic
                                                                                | BindingFlags.Instance);

            if (_methArrangeHiddenVisual == null)
                throw new InvalidOperationException("ArrangeHiddenVisual() method" + Properties.Resources.WPF_LIB_NAME);
        }

        #region Private Fields

        /// <summary>
        /// The rate at which the Win32 control is rendered.
        /// This value is in miliseconds.
        /// </summary>
        /// 
        private readonly int _rendererInterval = 33;

        private RasterRendererElement _rasterRendererElement;

        /// <summary>
        /// The Win32 control that is being rendered.
        /// </summary>
        /// 
        private Winforms.Control _contentControl;

        /// <summary>
        /// Reference to the WPF <see cref="Window"/> that this control belongs to.
        /// </summary>
        /// 
        private Window _window;

        /// <summary>
        /// The decorator for interactive 3D support. (3DTools)
        /// When this is not null the Win32 control is in 3D space.
        /// </summary>
        /// 
        private Interactive3DDecorator _decorator;

        /// <summary>
        /// GDI bitmap used as a helper in rendering
        /// the Win32 control
        /// </summary>
        private GDI.Bitmap _gdiBitmap;

        /// <summary>
        /// GDI Graphics class to paint to the
        /// GDI bitmap
        /// </summary>
        private GDI.Graphics _gdiBitmapGraphics;

        /// <summary>
        /// The HWND of the WPF window
        /// </summary>
        private HwndSource _hWndSource;

        /// <summary>
        /// The Win32 form that will hold the Win32 control that will be rendered.
        /// </summary>
        /// 
        internal InteropForm _interopForm;

        /// <summary>
        /// The last scan0 (first pixel in the BitmapSource's buffer) used.
        /// Used to check to see if the BitmapSource's buffer changes. If it doesn't change then
        /// don't rebuild the <see cref="GDI.Bitmap"/> wrapper class.
        /// </summary>
        /// 
        private IntPtr _prevScan0;

        /// <summary>
        /// The timer used to drive the Win32 rendering. The Win32 window is only rendered by the timer, not for
        /// every WPF screen refresh. The PaintWindow() call is *very* slow.
        /// </summary>
        /// 
        private DispatcherTimer _rendererTimer;

        /// <summary>
        /// 
        /// </summary>
        private MouseHooks.MouseHookManager _systemMouseHook;

        private bool _allowPaint;

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="Win32HostRenderer"/> class.
        /// </summary>
        /// 
        public Win32HostRenderer()
        {
            /* Hook into events */
            Loaded += delegate
            {
                RegisterEventHandlers(true);
            };

            Unloaded += delegate
            {
                RegisterEventHandlers(false);
            };

            _rasterRendererElement = new InteropBitmapRenderer();

            /*
            Application.Current.Exit += delegate
            {
                RegisterEventHandlers(false);
            };
             */
        }

        #region Dependency Properties

        #region IsInteractive

        /// <summary>
        /// IsInteractive Dependency Property
        /// </summary>
        public static readonly DependencyProperty IsInteractiveProperty =
            DependencyProperty.Register("IsInteractive", typeof(bool), typeof(Win32HostRenderer),
                new FrameworkPropertyMetadata(true,
                    new PropertyChangedCallback(OnIsInteractiveChanged)));

        public bool IsInteractive
        {
            get { return (bool)GetValue(IsInteractiveProperty); }
            set { SetValue(IsInteractiveProperty, value); }
        }

        /// <summary>
        /// Handles changes to the IsInteractive property.
        /// </summary>
        private static void OnIsInteractiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((Win32HostRenderer)d).OnIsInteractiveChanged(e);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the IsInteractive property.
        /// </summary>
        protected virtual void OnIsInteractiveChanged(DependencyPropertyChangedEventArgs e)
        {
            if (_interopForm != null)
                _interopForm.Enabled = (bool)e.NewValue;
        }

        #endregion
        #region RefreshInterval



        /// <summary>
        /// RefreshInterval Dependency Property
        /// </summary>
        public static readonly DependencyProperty RefreshIntervalProperty =
            DependencyProperty.Register("RefreshInterval", typeof(int), typeof(Win32HostRenderer),
                new FrameworkPropertyMetadata(33,
                    new PropertyChangedCallback(OnRefreshIntervalChanged)));

        public int RefreshInterval
        {
            get { return (int)GetValue(RefreshIntervalProperty); }
            set { SetValue(RefreshIntervalProperty, value); }
        }

        /// <summary>
        /// Handles changes to the RefreshInterval property.
        /// </summary>
        private static void OnRefreshIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((Win32HostRenderer)d).OnRefreshIntervalChanged(e);
        }

        /// <summary>
        /// Provides derived classes an opportunity to handle changes to the RefreshInterval property.
        /// </summary>
        protected virtual void OnRefreshIntervalChanged(DependencyPropertyChangedEventArgs e)
        {
            _rendererTimer.Interval = TimeSpan.FromMilliseconds((int)e.NewValue);
        }

        #endregion

        /// <summary>
        /// The dependency debug opacity property. See <see cref="DebugOpacity"/> property.
        /// </summary>
        /// 
        public static readonly DependencyProperty DebugOpacityProperty =
            DependencyProperty.Register(
                "DebugOpacity",
                typeof(double),
                typeof(Win32HostRenderer),
                new PropertyMetadata(0.2, OnDebugPropertyChanged));

        /// <summary>
        /// The dependency debug property. See <see cref="Debug"/> property.
        /// </summary>
        /// 
        public static readonly DependencyProperty DebugProperty =
            DependencyProperty.Register(
                "Debug",
                typeof(bool),
                typeof(Win32HostRenderer),
                new PropertyMetadata(false, OnDebugPropertyChanged));

        internal static void OnDebugPropertyChanged(Object sender, DependencyPropertyChangedEventArgs e)
        {
            Win32HostRenderer me = (Win32HostRenderer)sender;

            // if the property changes while the interop form is active update the form's opacity to refect the change.
            if (me.IsInteropFormEnabled)
                me._interopForm.Opacity = me.GetOpacity();
        }

        #endregion

        /// <summary>
        /// The Win32 <see cref="System.Windows.Forms.Form"/> instance that contains the Win32 control being rendered.
        /// </summary>
        /// 
        public InteropForm InteropForm
        {
            get { return _interopForm; }
        }


        /// <summary>
        /// Gets or sets the debug opacity of the control when the <see cref="Debug"/> property is set to <c>true</c>.
        /// </summary>
        /// <value>The debug opacity of the control when the <see cref="Debug"/> property is set to <c>true</c>.</value>
        /// 
        public double DebugOpacity
        {
            get { return (double)GetValue(DebugOpacityProperty); }
            set { SetValue(DebugOpacityProperty, value); }
        }

        /// <summary>
        /// Gets or sets the debug state.
        /// </summary>
        /// <remarks>
        /// When <see cref="Debug"/> is <c>true</c> the Win32 control will be overlayed allowing you to see how the
        /// mouse events are been processed.
        /// </remarks>
        /// 
        public bool Debug
        {
            get { return (bool)GetValue(DebugProperty); }
            set { SetValue(DebugProperty, value); }
        }

        /// <summary>
        /// Gets or sets the Win32 <see cref="Winforms.Control"/> to be rendered.
        /// </summary>
        /// 
        public Winforms.Control ContentControl
        {
            get { return _contentControl; }
            set
            {
                InitializeInteropForm(value);
                InitializeRendererTimer();
            }
        }

        protected override Size ArrangeOverride(Size arrangeBounds)
        {
            if (_contentControl != null)
            {
                _contentControl.Size = new GDI.Size((int)arrangeBounds.Width, (int)arrangeBounds.Height);
                return arrangeBounds;
            }
            else
                return base.ArrangeOverride(arrangeBounds);
        }

        protected override Size MeasureOverride(Size constraint)
        {
            if (_contentControl != null)
            {
                if (double.IsPositiveInfinity(constraint.Width))
                    constraint.Width = _contentControl.Width;

                if (double.IsPositiveInfinity(constraint.Height))
                    constraint.Height = _contentControl.Width;

                return constraint;
            }
            else
                return base.MeasureOverride(constraint);
        }

        protected override void OnGotFocus(RoutedEventArgs e)
        {
            base.OnGotFocus(e);

            if (_contentControl != null)
                _contentControl.Focus();
        }

        //TODO: change this to rather use the control's background brush.
        /// <summary>
        /// Called by WPF when a the control needs to be re-rendered.
        /// </summary>
        /// <param name="drawingContext">The <see cref="DrawingContext"/>.</param>
        /// 
        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            
            //if (_bitmapSource != null)
                drawingContext.DrawImage(_rasterRendererElement.RenderedImage.Source, new Rect(0, 0, DesiredSize.Width, DesiredSize.Height));
        }

        /// <summary>
        /// Overridden to only enable the interop form when the mouse has entered the interop's control space.
        /// </summary>
        /// <param name="e">The <see cref="MouseEventArgs"/> instance containing the event data.</param>
        /// 
        /// <logic>
        /// 	<item>1. Position and enable the interop form.</item>
        /// </logic>
        /// 
        protected override void OnMouseEnter(MouseEventArgs e)
        {
            base.OnMouseEnter(e);

            // 1
            SetInteropFormEnabled(true);
        }

        private System.Drawing.Size _bitmapSize;

        /// <summary>
        /// Renders the Win32 control to WPF space via the <see cref="BitmapBuffer"/>.
        /// </summary>
        /// 
        private void RenderWinformControl()
        {
            if (_contentControl == null)
                return;

            UIElement parent = GetParent();
            if ((parent == null) || (!parent.IsVisible))
                return;


            if(_contentControl.Width != _bitmapSize.Width || _contentControl.Height != _bitmapSize.Height)
            {
                if (_gdiBitmapGraphics != null)
                    _gdiBitmapGraphics.Dispose();

                if (_gdiBitmap != null)
                    _gdiBitmap.Dispose();

                _gdiBitmapGraphics = null;
                _gdiBitmap = null;
            }
           

            if(_gdiBitmap == null)
            {
                _bitmapSize = new System.Drawing.Size(_contentControl.Width, _contentControl.Height);

                /* Wrap the BitmapSource's pixels into
                 * a GDI bitmap */
                _gdiBitmap = new Bitmap(_bitmapSize.Width,
                                        _bitmapSize.Height,
                                        PixelFormat.Format32bppRgb
                                        );

                /* Initialize a graphics class for painting */
                _gdiBitmapGraphics = GDI.Graphics.FromImage(_gdiBitmap);
            }
           

            /* Paint the Win32 control to the GDI bitmap */
            PaintWinformControl(_gdiBitmapGraphics, _contentControl);

            /* Tell WPF to redraw */
            InvalidateVisual();
        }

        /// <summary>
        /// Paints a control to a GDI Graphics class.
        /// </summary>
        /// <param name="graphics">The GDI Graphics class to paint too.</param>
        /// <param name="control">The Win32 <see cref="Winforms.Control"/> to paint.</param>
        /// 
        private void PaintWinformControl(GDI.Graphics graphics, Winforms.Control control)
        {
            /* Get the control's HWND */
            IntPtr hWnd = control.Handle;

            /* Get the device context */
            IntPtr hDC = graphics.GetHdc();

            _allowPaint = true;
            /* Make a Win32 call to paint to the DC */
            User32.PrintWindow(hWnd, hDC, 0);

            _allowPaint = false;

            /* Free some resources */
            graphics.ReleaseHdc(hDC);

            var bmpRect = new Rectangle(0, 0, _bitmapSize.Width, _bitmapSize.Height);

            var bmpData = _gdiBitmap.LockBits(bmpRect,ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb );

            _rasterRendererElement.RenderFrame(new RenderedFrameEventArgs(bmpData.Scan0, bmpData.Stride * bmpData.Height, bmpData.Width, bmpData.Height));

            _gdiBitmap.UnlockBits(bmpData);

            //_gdiBitmap.Save(@"c:\users\jmorrill\desktop\test.bmp");
        }

        #region Windows Messages

        /// <summary>
        /// Pre-process messages sent to the WPF <see cref="Window"/>.
        /// </summary>
        /// <param name="hwnd">The Win32 windows handle.</param>
        /// <param name="msg">The message.</param>
        /// <param name="wParam">The message wParam.</param>
        /// <param name="lParam">The message lParam.</param>
        /// <param name="handled">if set to <c>true</c> flags the message as handled.</param>
        /// <returns><see cref="IntPtr.Zero"/> to let the message continue back to the WPF <see cref="Window"/>.</returns>
        /// 
        protected IntPtr HandleWindowMessageHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            switch (msg)
            {
                /*
                case Win32.User32.WM_NCACTIVATE:
                    if (wParam == IntPtr.Zero)
                        Win32.User32.PostMessage(hwnd, Win32.User32.WM_NCACTIVATE, 1, 0);
                    break;
                 */
                // As soon as the window is requested to be closed disable the system message hook.
                // This is done because sometimes messages get through as the application is shutting down and this
                // causes an OS shutdown error.
                case User32.WM_CLOSE:
                    _systemMouseHook.Dispose();
                    _systemMouseHook = null;
                    break;
            }

            return IntPtr.Zero; // let the message continue to the actual WPF window.
        }

        /// <summary>
        /// Processes the mouse messages before any windows get this message.
        /// </summary>
        /// <param name="code">nCode</param>
        /// <param name="mainParameter">wParam</param>
        /// <param name="additionalParameter">lParam</param>
        /// <param name="point">point</param>
        /// <param name="extraInfo">dwExtraInfo</param>
        /// <returns>if <b>false</b> the destination window will receive the message; otherwise the message will be swallowed.</returns>
        /// <remarks>
        /// <note>
        /// All windows messages for every application running on your computer will be routed though this handler so it has to be fast.
        /// </note>
        /// The reason you want to process messages at this insane override level is because the <see cref="Win32HostRenderer"/>
        /// control wil want to preview messages that arean't destined for it (sneaky style) :
        /// <para>
        /// 1. When the mouse button first goes down, the interop form might not be visible and might require mouse capture.
        /// It's too late for the interop to get mouse capture if the WPF <see cref="Window"/> has already inherited this
        /// message. (That is you can't override Window.OnMouseDown() -> show interop form.
        /// You need to trap the message, show the form (under the mouse pointer) and let the message continue in the
        /// message loop chain so the message will be picked up by the intefop form, even though the form wasn't actually
        /// visible when the mouse button first went down.
        /// </para>
        /// <para>
        /// 2. Another reason is when the interop form is open, it's open above the WPF <see cref="Window"/> and the WPF Window
        /// will never receive any mouse messages (not even preview messages), so you need a way to preview the messages and update the interop form's
        /// position even and then let the Win32 control contine to receive those messages.
        /// (Understand that when you're dragging a text selection area in the WebBrowser control messages's arean't going
        /// anyway except to the Win32 control because a drap procedure has full mouse capture.)
        /// </para>
        /// 
        /// </remarks>
        /// <logic>
        /// 	<item>1. When the mouse goes down and it's over <b>this</b> instance of the <see cref="Win32HostRenderer"/>.
        ///           enable and position the interop form immediately.</item>
        ///     <item>2. If the interop form is enabled and the mouse moves off it,
        ///           disable it (unless it has mouse capture (User32.GetCapture() != 0), because then it's probably in a drag operation).</item>
        /// </logic>
        /// 
        #region IMouseHookUser Members

        bool IMouseHookUser.SystemMouseHookProc(int code,
                                         int mainParameter,
                                         int additionalParameter,
                                         GDI.Point point,
                                         int extraInfo)
        {
            switch (mainParameter)
            {
                // 1
                case User32.WM_LBUTTONDOWN:
                case User32.WM_RBUTTONDOWN:
                case User32.WM_MBUTTONDOWN:
                    if (Mouse.DirectlyOver == this)
                    {
                        SetInteropFormEnabled(true);
                    }
                    break;
                case User32.WM_LBUTTONUP:
                case User32.WM_RBUTTONUP:
                case User32.WM_MBUTTONUP:
                    UpdateInteropFormEnabled();
                    break;
                case User32.WM_MOUSEMOVE:
                    // 2
                    if (User32.GetCapture() == IntPtr.Zero) // no capture
                    {
                        UpdateInteropFormEnabled();
                    }

                    if (_decorator != null)
                    {
                        if (IsInteropFormEnabled)
                        {
                            // reposition the hidden visual
                            if (_decorator.Viewport3D != null)
                            {
                                GDI.Point screenPos = Winforms.Cursor.Position;

                                Point decTopLeft = _decorator.Viewport3D.PointToScreen(new Point());

                                Point formPos = new Point(screenPos.X - decTopLeft.X, screenPos.Y - decTopLeft.Y);

                                _methArrangeHiddenVisual.Invoke(_decorator, new object[] { formPos, false });
                            }
                        }
                    }
                    break;
            }

            return false;
        }

        #endregion

        private double GetOpacity()
        {
            if (Debug)
                return DebugOpacity;
            else
                // the WPF opacity is a factor (0-1) of the actual Win32 aplha values which is (0-255)
                // so to enabled the interop form you need to set the opacity to 1 (byte value), because 0 makes the form transparent you
                // user interaction.
                return 1f / 255; 
        }

        private Interactive3DDecorator GetDecorator()
        {
            return _propInheritanceParent.GetValue(this, new object[] { }) as Interactive3DDecorator;
        }

        private UIElement GetParent()
        {
            UIElement result = GetDecorator();
            if (result == null)
                result = (this.Parent as UIElement);

            return result;
        }

        #endregion

        #region InteropForm

        /// <summary>
        /// Tears down the references to the
        /// interop form and frees resources
        /// </summary>
        private void TearDownInteropForm()
        {
            if (_contentControl != null)
            {
                /* Unhook from events */
                _contentControl.SizeChanged -= _contentControl_Changed;
                _contentControl.Disposed -= _contentControl_Disposed;
            }

            if (_interopForm != null)
            {
                Winforms.Application.RemoveMessageFilter(this);
                // Free the resources
                _interopForm.Dispose();
                // see _interopForm_Disposed()
                // _systemMouseHook disposed there

                _interopForm = null;
                _rasterRendererElement.Dispose();
                // Tear down our bitmap resources
                TearDownBitmap();
            }
        }

        /// <summary>
        /// Initializes and sets the default state of 
        /// the Win32 form that will contain the Win32 control that will be rendered.
        /// </summary>
        /// <param name="newContentControl">The Win32 control to initialize with the form</param>
        /// 
        private void InitializeInteropForm(Winforms.Control newContentControl)
        {
            /* Tear down any existing setup */
            TearDownInteropForm();

            /* Keep reference to the Win32 control */
            _contentControl = newContentControl;

            if ((newContentControl != null) && !newContentControl.IsDisposed)
            {
                _interopForm = new InteropForm();
                _interopForm.Enabled = false;
                _interopForm.Opacity = 0.00;
                _interopForm.Controls.Add(_contentControl);
                _interopForm.StartPosition = Winforms.FormStartPosition.Manual;
                _interopForm.Disposed += _interopForm_Disposed;
                _interopForm.Activated += _interopForm_Activated;
                _interopForm.Enabled = IsInteractive;

                newContentControl.SizeChanged += _contentControl_Changed;
                newContentControl.Disposed += _contentControl_Disposed;

                _contentControl_Changed(null, null);

                _interopForm.Show();

                _systemMouseHook = MouseHooks.HookMouse(this, true);
                Winforms.Application.AddMessageFilter(this);
            }
        }

        private void _interopForm_Activated(object sender, EventArgs e)
        {
            if (_window != null)
                User32.PostMessage(_hWndSource.Handle, User32.WM_NCACTIVATE, 1, 0);
        }

        private void _interopForm_Disposed(object sender, EventArgs e)
        {
            if (_systemMouseHook != null)
            {
                _systemMouseHook.Dispose();
                _systemMouseHook = null;
            }
        }

        private void _contentControl_Changed(object sender, EventArgs e)
        {
            TearDownBitmap();
        }

        private void _contentControl_Disposed(object sender, EventArgs e)
        {
            TearDownInteropForm();
        }

        /// <summary>
        /// Positions the interop form over the WPF render of the Win32 control (even when it's not visible).
        /// </summary>
        /// 
        private void PositionInteropFormOverRender()
        {
            if ((_interopForm != null) && (_window != null))
            {
                // can only position in the OnRender() method if it's attached to a source 
                // the Interactive3D stuff sometimes detaches this control from the PresentationSource.
                if (PresentationSource.FromVisual(this) != null)
                {
                    Point screenPos = PointToScreen(new Point());

                    if (_decorator != null)
                    {
                        GDI.Rectangle rect = _interopForm.Bounds;
                        rect.Size = _interopForm.Controls[0].Size;

                        System.Windows.Point decPos = _decorator.PointFromScreen(screenPos);

                        if ((decPos.X < _decorator.ActualWidth) && (decPos.Y < _decorator.ActualHeight))
                        {
                            rect.Location = new GDI.Point((int)screenPos.X, (int)screenPos.Y);
                        }

                        _interopForm.Bounds = rect;
                    }
                    else
                    {
                        GDI.Rectangle rect = new GDI.Rectangle();
                        rect.Location = new GDI.Point((int)screenPos.X, (int)screenPos.Y);
                        rect.Size = _contentControl.Size;

                        /* - don't bother to clip the window (control might be rotated).
                        Size size = _window.RenderSize;
                        rect.Intersect(
                            new GDI.Rectangle(
                                (int)_window.Left, (int)_window.Top,
                                (int)size.Width, (int)size.Height)
                            );
                        */

                        _interopForm.Bounds = rect;
                    }
                }
            }
        }

        private void SetInteropFormEnabled(bool enabled)
        {
            if (_interopForm != null)
            {
                if (enabled)
                {
                    PositionInteropFormOverRender();
                    _interopForm.Opacity = GetOpacity();
                }
                else
                    _interopForm.Opacity = 0;
            }
        }

        /// <summary>
        /// Updates the interop form enabled state.
        /// </summary>
        /// <logic>
        /// 	<item>1. If the mouse pointer is no longer over the control disable it.</item>
        /// </logic>
        /// 
        private void UpdateInteropFormEnabled()
        {
            if (IsInteropFormEnabled)
            {
                GDI.Point screenPos = Winforms.Cursor.Position;
                HitTestResult h = VisualTreeHelper.HitTest(_window, _window.PointFromScreen(new Point(screenPos.X, screenPos.Y)));
                // 1
                if ((h == null) || (h.VisualHit != this))
                {
                    SetInteropFormEnabled(false);
                }
            }
        }

        private bool IsInteropFormEnabled
        {
            get { return (_interopForm != null) && (_interopForm.Opacity > 0); }
        }

        #endregion

        #region RenterTimer

        /// <summary>
        /// Uninitializes the Win32 render timer
        /// </summary>
        private void TearDownRenderTimer()
        {
            if (_rendererTimer != null)
            {
                _rendererTimer.IsEnabled = false;
                _rendererTimer = null;
            }
        }

        /// <summary>
        /// Initializies the render timer
        /// </summary>
        private void InitializeRendererTimer()
        {
            /* Destroy any existing setup */
            TearDownRenderTimer();

            /* Create a new timer */
            _rendererTimer = new DispatcherTimer();

            /* Set the timer's interval */
            _rendererTimer.Interval = new TimeSpan(0, 0, 0, 0, (int)RefreshIntervalProperty.DefaultMetadata.DefaultValue);

            /* Set the callback of each timer tick */
            _rendererTimer.Tick += _rendererTimer_Tick;

            /* Start the timer */
            _rendererTimer.Start();
        }

        private void _rendererTimer_Tick(object sender, EventArgs e)
        {
            RenderWinformControl();
            PositionInteropFormOverRender();
        }

        #endregion

        #region Window

        /// <summary>
        /// Registers the event handlers on the WPF <see cref="Window"/> to determine the window is
        /// minized, etc.
        /// </summary>
        /// <param name="Attach">if set to <c>true</c> attaches the events; otherwise detaches the events.</param>
        /// 
        private void RegisterEventHandlers(bool Attach)
        {
            Window newWindow = Window.GetWindow(this);
            if (newWindow != _window)
            {
                if (_window != null)
                {
                    _window.LocationChanged -= _window_Changed;
                    _window.SizeChanged -= _window_Changed;
                    _window.StateChanged -= _window_Changed;

                    _hWndSource.RemoveHook(HandleWindowMessageHook);
                    _hWndSource = null;

                    _decorator = null;
                }

                _window = newWindow;

                if ((_window != null) && Attach)
                {
                    _decorator = GetDecorator();

                    _hWndSource = (HwndSource)HwndSource.FromVisual(_window);
                    _hWndSource.AddHook(HandleWindowMessageHook);

                    _window.LocationChanged += _window_Changed;
                    _window.SizeChanged += _window_Changed;
                    _window.StateChanged += _window_Changed;
                }
            }
        }

        private void _window_Changed(object sender, EventArgs e)
        {
            PositionInteropFormOverRender();
        }

        /*
        private void _currentWindow_ActivationChanged(object sender, EventArgs e)
        {
             _interopForm.Opacity = 0;
             _interopForm.Opacity = 0.01;
        }
        */

        #endregion

        #region Bitmap

        /// <summary>
        /// Unreferences and cleans up
        /// the BitmapSource and GDI objects
        /// </summary>
        private void TearDownBitmap()
        {
            if (_gdiBitmap != null)
            {
                _gdiBitmap.Dispose();
                _gdiBitmap = null;
            }

            if (_gdiBitmapGraphics != null)
            {
                _gdiBitmapGraphics.Dispose();
                _gdiBitmapGraphics = null;
            }
        }

        #endregion

        #region IMessageFilter Members

        bool Winforms.IMessageFilter.PreFilterMessage(ref System.Windows.Forms.Message m)
        {
            switch (m.Msg)
            {
                case User32.WM_ERASEBKGND:
                case User32.WM_PAINT:
                    if ((_contentControl != null) &&
                        (User32.IsParentWindow(m.HWnd, new HandleRef(_contentControl, _contentControl.Handle))))
                    {
                        return !_allowPaint;
                    }
                    else
                        return false;

                default:
                    return false;
            }
        }

        #endregion

        #region INotifyPropertyChanged Members

        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }
}